package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.Goods;
import com.atguigu.gmall.model.list.SearchResponseAttrVo;
import com.atguigu.gmall.model.list.SearchResponseTmVo;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/***
 * 商品搜索service层实现类
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 根据条件搜索
     *
     * @param searchParams
     * @return
     */
    @Override
    public Map<String, Object> search(Map<String, Object> searchParams) {
        try {
            //封装查询请求对象
            SearchRequest searchRequest = this.buildSearchParams(searchParams);

            //进行搜索操作
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            //解析查询结果
            Map result = this.parseSearchResponse(searchResponse);

            //返回结果
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }

        //返回
        return null;
    }

    /**
     * 解析搜索出的数据
     *
     * @param searchResponse
     * @return
     */
    private Map parseSearchResponse(SearchResponse searchResponse) {
        //声明一个用装所有返回数据的map
        Map<String, Object> result = new HashMap<>();
        //声明一个用来装所有goods的集合
        ArrayList<Goods> goodsList = new ArrayList<>();

        //1 获取命中的数据
        SearchHits hits = searchResponse.getHits();
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            //获取每条数据
            SearchHit next = iterator.next();
            String sourceAsString = next.getSourceAsString();
            //反序列化
            Goods goods = JSONObject.parseObject(sourceAsString, Goods.class);
            //获取高亮部分数据
            HighlightField highlight = next.getHighlightFields().get("title");
            if(highlight != null){
                Text[] highlightFragments = highlight.getFragments();
                    if(highlightFragments != null && highlightFragments.length>0){
                        //拼接
                        String title = "";
                        for (Text highlightFragment : highlightFragments) {
                            title += highlightFragment;
                        }
                        //替换数据
                        goods.setTitle(title);
                    }
            }
            //添加数据到集合
            goodsList.add(goods);
        }
        //获取总命中的数据的数量
        long totalHits = hits.getTotalHits();

        //2 获取聚合的所有数据
        Aggregations aggregations = searchResponse.getAggregations();
        //2.1 获取品牌聚合的结果
        List<SearchResponseTmVo> searchResponseTmVoList = this.getTradeMarkFromAggResult(aggregations);
        //2.2 获取平台属性聚合的结果
        List<SearchResponseAttrVo> searchResponseAttrVoList = this.getAttrFromAggResult(aggregations);

        //3 封装返回数据
        result.put("totalHits",totalHits);
        result.put("goodsList", goodsList);
        result.put("searchResponseTmVoList", searchResponseTmVoList);
        result.put("searchResponseAttrVoList", searchResponseAttrVoList);

        //4 返回
        return result;
    }

    /**
     * 获取平台属性聚合的结果
     *
     * @param aggregations
     * @return
     */
    private List<SearchResponseAttrVo> getAttrFromAggResult(Aggregations aggregations) {
        //1 获取第一层的分桶的数据
        ParsedNested attrsAgg = aggregations.get("attrsAgg");

        //2 获取以平台属性id分桶数据，第二层的分桶数据
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attrIdAgg");
        //遍历
        List<SearchResponseAttrVo> SearchResponseAttrVoList = attrIdAgg.getBuckets().stream().map(attrIdBucket -> {
            //声明一个用来封装平台属性聚合数据的vo对象
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
            //获取平台属性id
            long attrId = attrIdBucket.getKeyAsNumber().longValue();
            searchResponseAttrVo.setAttrId(attrId);

            //2.1 获取以平台属性分桶数据
            ParsedStringTerms attrNameAgg = attrIdBucket.getAggregations().get("attrNameAgg");
            if (!attrNameAgg.getBuckets().isEmpty()) {
                //获取平台属性名字
                String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
                searchResponseAttrVo.setAttrName(attrName);
            }

            //2.2 获取以平台属性值聚合的数据
            ParsedStringTerms attrValueAgg = attrIdBucket.getAggregations().get("attrValueAgg");
            //遍历，一个平台属性 --> 多个平台属性值
            List<String> attrValueList = attrValueAgg.getBuckets().stream().map(attrValueAggBucket -> {
                //获取平台属性值
                String attrValue = attrValueAggBucket.getKeyAsString();
                return attrValue;
            }).collect(Collectors.toList());
            searchResponseAttrVo.setAttrValueList(attrValueList);

            return searchResponseAttrVo;
        }).collect(Collectors.toList());

        //3 返回
        return SearchResponseAttrVoList;
    }

    /**
     * 获取品牌聚合的结果
     *
     * @param aggregations
     * @return
     */
    private List<SearchResponseTmVo> getTradeMarkFromAggResult(Aggregations aggregations) {
        //1 获取第一层聚合的结果
        ParsedLongTerms aggTmId = aggregations.get("aggTmId");
        //遍历分桶后的数据
        List<SearchResponseTmVo> searchResponseTmVoList = aggTmId.getBuckets().stream().map(aggTmIdBucket -> {
            //声明一个用来封装品牌聚合的数据的vo对象
            SearchResponseTmVo searchResponseTmVo = new SearchResponseTmVo();
            //获取到品牌id
            long tmId = aggTmIdBucket.getKeyAsNumber().longValue();
            searchResponseTmVo.setTmId(tmId);
            //获取aggTmName子聚合数据
            ParsedStringTerms aggTmName = aggTmIdBucket.getAggregations().get("aggTmName");
            if (!aggTmName.getBuckets().isEmpty()) {
                String tmName = aggTmName.getBuckets().get(0).getKeyAsString();
                searchResponseTmVo.setTmName(tmName);
            }
            //获取aggTmLogoUrl子聚合数据
            ParsedStringTerms aggTmLogoUrl = aggTmIdBucket.getAggregations().get("aggTmLogoUrl");
            if (!aggTmLogoUrl.getBuckets().isEmpty()) {
                String tmLogoUrl = aggTmLogoUrl.getBuckets().get(0).getKeyAsString();
                searchResponseTmVo.setTmLogoUrl(tmLogoUrl);
            }
            //返回searchResponseTmVo
            return searchResponseTmVo;
        }).collect(Collectors.toList());

        //2 返回数据
        return searchResponseTmVoList;
    }

    /**
     * 封装查询请求对象
     *
     * @param searchParams
     * @return
     */
    private SearchRequest buildSearchParams(Map<String, Object> searchParams) {
        //声明查询请求对象，指定要搜索的索引
        SearchRequest searchRequest = new SearchRequest("goods_java0107");

        //声明一个搜索条件构建器
        SearchSourceBuilder builder = new SearchSourceBuilder();
        //声明一个组合查询构建器 must=and  should=or mustnot=not
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //关键字查询构建
        Object keyword = searchParams.get("keyword");
        if (!StringUtils.isEmpty(keyword)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", keyword));
        }

        //分类查询构建 category = 1:手机
        String category = (String) searchParams.get("category");
        if (!StringUtils.isEmpty(category)) {
            //切分
            String[] split = category.split(":");
            boolQueryBuilder.must(QueryBuilders.termQuery("category3Id", split[0]));
        }

        //品牌查询构建 tradeMark=1:华为
        String tradeMark = (String) searchParams.get("tradeMark");
        if (!StringUtils.isEmpty(tradeMark)) {
            //切分
            String[] split = tradeMark.split(":");
            boolQueryBuilder.must(QueryBuilders.termQuery("tmId", split[0]));
        }

        //价格查询构建 1000-2000元 3000元以上
        String price = (String) searchParams.get("price");
        if (!StringUtils.isEmpty(price)) {
            //去除不必要的文字，留下数字
            price = price.replace("元", "").replace("以上", "");
            //切分
            String[] split = price.split("-");
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(split[0]));
            if (split.length > 1) {
                //有右边
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lt(split[1]));
            }
        }

        //平台属性查询构建 attr_颜色=23:蓝色
        //遍历搜索参数map
        searchParams.entrySet().forEach(entry -> {
            //获取参数的名字
            String key = entry.getKey();
            if (key.startsWith("attr_")) {
                //获取到搜索参数为平台属性的数据
                String attrIdAndAttrName = (String) entry.getValue();
                //切分 0-平台属性的id  1-平台属性的值
                String[] split = attrIdAndAttrName.split(":");
                //平台属性查询构建
                BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.termQuery("attrs.attrId", split[0]))
                        .must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                boolQueryBuilder.must(QueryBuilders.nestedQuery("attrs", nestedBoolQueryBuilder, ScoreMode.None));
            }
        });

        //填装所有查询条件
        builder.query(boolQueryBuilder);

        //品牌聚合
        builder.aggregation(
                AggregationBuilders.terms("aggTmId").field("tmId")
                        .subAggregation(AggregationBuilders.terms("aggTmName").field("tmName"))
                        .subAggregation(AggregationBuilders.terms("aggTmLogoUrl").field("tmLogoUrl"))
                        .size(100)
        );

        //平台属性聚合
        builder.aggregation(
                AggregationBuilders.nested("attrsAgg", "attrs")
                        .subAggregation(
                                AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))
                                        .size(100)
                        )
        );

        //排序 sortField=id&sortRule=DESC
        String sortField = (String) searchParams.get("sortField");
        String sortRule = (String) searchParams.get("sortRule");
        if (!StringUtils.isEmpty(sortField) && !StringUtils.isEmpty(sortRule)) {
            builder.sort(sortField, SortOrder.valueOf(sortRule));
        } else {
            //默认新品排序
            builder.sort("id", SortOrder.DESC);
        }

        //分页
        builder.size(50);
        //校验页码
        String pageNum = (String) searchParams.get("pageNum");
        int pageNumCheck = this.getPageNum(pageNum);
        builder.from((pageNumCheck-1)*50);

        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<font style=color:red>");
        highlightBuilder.postTags("</font>");
        builder.highlighter(highlightBuilder);

        //封装所有搜索要求，并返回
        return searchRequest.source(builder);
    }

    /**
     * 返回有效页码
     *
     * @param pageNum
     * @return
     */
    private static int getPageNum(String pageNum) {
        try{
            int pageN = Integer.parseInt(pageNum);
            //防止负数
            pageN = pageN <= 0 ? 1 : pageN;
            //防止超出范围
            pageN = pageN>199?199:pageN;
            //返回
            return pageN;
        }catch (Exception e){
            return 1;
        }
    }
}
